﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml;

namespace GraphTools.OSM
{
    public class OSMHandler
    {
        public static void WriteFile(string filename, ICollection<OSMNode> nodes, ICollection<OSMWay> ways, ICollection<OSMRelation> relations)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            //settings.NewLineOnAttributes = true;
            using (XmlWriter writer = XmlWriter.Create(filename, settings))
            {
                writer.WriteStartDocument();
                writer.WriteStartElement("osm");
                writer.WriteAttributeString("version","0.6");
                Version vrs = System.Reflection.Assembly.GetEntryAssembly().GetName().Version;
                writer.WriteAttributeString("generator", "CLC Simplifier v" + vrs.Major + "." + vrs.Minor + "." + vrs.Build + " by Plutoz");
            //write nodes
                foreach (OSMNode n in nodes)
                {
                    writer.WriteStartElement("node");
                    writer.WriteAttributeString("id", XmlConvert.ToString(n.ID));
                    writer.WriteAttributeString("version", XmlConvert.ToString(n.Version));
                    writer.WriteAttributeString("timestamp", XmlConvert.ToString(n.Timestamp));
                    writer.WriteAttributeString("lat", XmlConvert.ToString(n.Latitude));
                    writer.WriteAttributeString("lon", XmlConvert.ToString(n.Longitude));
                    foreach (var tag in n.Tags)
                    {
                        writer.WriteStartElement("tag");
                        writer.WriteAttributeString("k", tag.Key);
                        writer.WriteAttributeString("v", tag.Value);
                        writer.WriteEndElement();
                    }
                    writer.WriteEndElement();
                }
            //write ways
                foreach (OSMWay w in ways)
                {
                    writer.WriteStartElement("way");
                    writer.WriteAttributeString("id", XmlConvert.ToString(w.ID));
                    writer.WriteAttributeString("version", XmlConvert.ToString(w.Version));
                    writer.WriteAttributeString("timestamp", XmlConvert.ToString(w.Timestamp));
                    foreach (int reference in w.Nodes)
                    {
                        writer.WriteStartElement("nd");
                        writer.WriteAttributeString("ref", XmlConvert.ToString(reference));
                        writer.WriteEndElement();
                    }
                    foreach (var tag in w.Tags)
                    {
                        writer.WriteStartElement("tag");
                        writer.WriteAttributeString("k",tag.Key);
                        writer.WriteAttributeString("v",tag.Value);
                        writer.WriteEndElement();
                    }
                    writer.WriteEndElement();
                }
            //write relations
                foreach (OSMRelation r in relations)
                {
                    writer.WriteStartElement("relation");
                    writer.WriteAttributeString("id", XmlConvert.ToString(r.ID));
                    writer.WriteAttributeString("version", XmlConvert.ToString(r.Version));
                    writer.WriteAttributeString("timestamp", XmlConvert.ToString(r.Timestamp));
                    foreach (var m in r.Members)
                    {
                        writer.WriteStartElement("member");
                        writer.WriteAttributeString("type", m.Type);
                        writer.WriteAttributeString("ref", XmlConvert.ToString(m.Reference));
                        writer.WriteAttributeString("role", m.Role);
                        writer.WriteEndElement();
                    }
                    foreach (var tag in r.Tags)
                    {
                        writer.WriteStartElement("tag");
                        writer.WriteAttributeString("k", tag.Key);
                        writer.WriteAttributeString("v", tag.Value);
                        writer.WriteEndElement();
                    }
                    writer.WriteEndElement();
                }
            //close parent
                writer.WriteEndElement();
                writer.WriteEndDocument();
            }
        }

        public static void ReadFile(string filename, out IDictionary<int,OSMNode> nodes, out IDictionary<int,OSMWay> ways, out IDictionary<int,OSMRelation> relations, bool processNodes = true, bool processWays = true, bool processRelations = true)
        {            
            XDocument doc = XDocument.Load(filename);

            nodes = processNodes?GetNodes(doc):new Dictionary<int,OSMNode>(0);
            ways = processWays ? GetWays(doc) : new Dictionary<int, OSMWay>(0);
            relations = processRelations ? GetRelations(doc) : new Dictionary<int, OSMRelation>(0);
        }

        //TODO: usereket, uid-t, changesetet nem olvassa be
        protected static IDictionary<int, OSMNode> GetNodes(XDocument doc)
        {
            IDictionary<int, OSMNode> result = new Dictionary<int, OSMNode>();
            foreach (XElement child in doc.Root.Elements("node"))
            {
                //processing tags
                IDictionary<string, string> tags = new Dictionary<string, string>(0);
                foreach (XElement t in child.Elements("tag"))
                {
                    tags.Add(t.Attribute("k").Value, t.Attribute("v").Value);
                }

                OSMNode tmp = new OSMNode(XmlConvert.ToInt32(child.Attribute("id").Value),
                    XmlConvert.ToInt32(child.Attribute("version").Value),
                    XmlConvert.ToDateTime(child.Attribute("timestamp").Value),
                    (float)XmlConvert.ToDouble(child.Attribute("lat").Value),
                    (float)XmlConvert.ToDouble(child.Attribute("lon").Value),
                    tags);
                result.Add(tmp.ID, tmp);
            }
            return result;
        }

        protected static IDictionary<int, OSMWay> GetWays(XDocument doc)
        {
            IDictionary<int, OSMWay> result = new Dictionary<int, OSMWay>(0);
            foreach (XElement child in doc.Root.Elements("way"))
            {
                //processing nodes
                IList<int> nodes = new List<int>();
                foreach (XElement n in child.Elements("nd"))
                {
                    nodes.Add(XmlConvert.ToInt32(n.Attribute("ref").Value));
                }

                //processing tags
                IDictionary<string, string> tags = new Dictionary<string, string>(0);
                foreach (XElement t in child.Elements("tag"))
                {
                    tags.Add(t.Attribute("k").Value, t.Attribute("v").Value);
                }


                OSMWay tmp = new OSMWay(XmlConvert.ToInt32(child.Attribute("id").Value),
                    XmlConvert.ToInt32(child.Attribute("version").Value),
                    XmlConvert.ToDateTime(child.Attribute("timestamp").Value),
                    nodes,
                    tags);
                result.Add(tmp.ID, tmp);
            }
            return result;
        }

        protected static IDictionary<int, OSMRelation> GetRelations(XDocument doc)
        {
            IDictionary<int, OSMRelation> result = new Dictionary<int, OSMRelation>(0);
            foreach (XElement child in doc.Root.Elements("relation"))
            {
                //processing members
                ICollection<OSMRelation.Member> members = new List<OSMRelation.Member>(0);
                foreach (XElement m in child.Elements("member"))
                {
                    OSMRelation.Member tmpMember = new OSMRelation.Member(m.Attribute("type").Value,
                        XmlConvert.ToInt32(m.Attribute("ref").Value),
                        m.Attribute("role").Value);
                    members.Add(tmpMember);
                }

                //processing tags
                IDictionary<string, string> tags = new Dictionary<string, string>(0);
                foreach (XElement t in child.Elements("tag"))
                {
                    tags.Add(t.Attribute("k").Value, t.Attribute("v").Value);
                }


                OSMRelation tmp = new OSMRelation(XmlConvert.ToInt32(child.Attribute("id").Value),
                    XmlConvert.ToInt32(child.Attribute("version").Value),
                    XmlConvert.ToDateTime(child.Attribute("timestamp").Value),
                    members,
                    tags);
                result.Add(tmp.ID, tmp);
            }
            return result;
        }
    }
}
